package ch.ethz.fcl.metrobuzz.algorithm.voronoi.Okabe;

import java.util.ArrayList;
import java.util.List;

import org.lwjgl.opengl.Display;
import org.lwjgl.opengl.GL11;

import ch.ethz.fcl.metrobuzz.MBContainer;
import ch.ethz.fcl.metrobuzz.controller.MBController;
import ch.ethz.fcl.metrobuzz.controller.external.ActionEvent;
import ch.ethz.fcl.metrobuzz.render.MBRenderer;
import ch.ethz.fcl.metrobuzz.spatialindex.object2d.Point2D;
import ch.ethz.fcl.metrobuzz.spatialindex.object2d.Rectangle;
import ch.ethz.fcl.metrobuzz.spatialindex.object3d.Point3D;
import ch.ethz.fcl.metrobuzz.tools.MBGL;
import ch.ethz.fcl.metrobuzz.viewer.MBViewer;
import ch.ethz.fcl.metrobuzz.viewer.MBViewer.ProjectionType;

/**
 * Test code of how to work with Additively Weighted Voronoi Diagram
 * 
 * @author ZengWei
 * 
 */
public class OkabeVoronoiTest {
	private MBContainer container;

	private List<Point3D> points;
	private List<Float> weights;

	public OkabeVoronoi voronoi;

	int screenWidth, screenHeight;

	public static void main(String[] args) {
		new OkabeVoronoiTest().run();
	}

	public OkabeVoronoiTest() {
		init();
	}

	private void run() {
		while (!Display.isCloseRequested()) {
			GL11.glPushMatrix();
			container.getViewer().update();

			container.getControl().update();

			update();
			render();

			GL11.glPopMatrix();

			Display.update();
			Display.sync(32);
		}
		Display.destroy();
		System.exit(0);
	}

	private void init() {
		points = new ArrayList<Point3D>();
		weights = new ArrayList<Float>();

		voronoi = new OkabeVoronoi(-0.5f, -0.5f, 0.5f, 0.5f);
		// voronoi = new AWVoronoi(-1, -1, -0.5f, -0.5f);

		container = new MBContainer();

		MBViewer view = new MBViewer(ProjectionType.Perspective, container);

		new MBRenderer(container);

		screenWidth = Display.getDesktopDisplayMode().getWidth();
		screenHeight = Display.getDesktopDisplayMode().getHeight();
		view.createDisplay(screenWidth, screenHeight, screenWidth, screenHeight,
				"Okabe voronoi test ");
		view.getCamera().initDistance(1.0f);
		view.getCamera().disablePan();

		new MBController(container);
	}

	private void update() {
		ActionEvent action = container.getControl().getMouseListener()
				.getRightMouse();

		if (action.getAction() == ActionEvent.ACTION_DOWN) {
			Point3D clickPoint = container
					.getViewer()
					.getProjection()
					.getWorldCoord(
							new Point2D(action.getEventX(), action.getEventY()),
							0);

			points.add(clickPoint);

			float weight = (float) Math.random() * 0.1f;
			weights.add(weight);

			ArrayList<OkabeVoronoiCell> generators1 = new ArrayList<OkabeVoronoiCell>();
			for (int i = 0; i < points.size(); i++)
				generators1.add(new OkabeVoronoiCell(points.get(i).getX(),
						points.get(i).getY(), weights.get(i)));

			voronoi.setGenerators(generators1);
			voronoi.generate();
			// if (voronoi.generators().size() >= 2)
			// for (Vec2D v : voronoi.edgeVertices()) {
			// WeightedVoronoiCell a = voronoi.generators().get(0);
			// WeightedVoronoiCell b = voronoi.generators().get(1);
			// // float distDiff = v.distanceTo(a) - v.distanceTo(b);
			// float distDiff = MBTools.distance(v.x, v.y, a.x, a.y)
			// - MBTools.distance(v.x, v.y, b.x, b.y);
			// float ratio = (Math.abs(distDiff) - Math.abs(a.w - b.w))
			// / Math.abs(distDiff);
			// // System.out.println(ratio);
			// if (Math.abs(ratio) >= 0.001f) {
			// System.out.println("1: a: " + a.toString() + " b: "
			// + b.toString());
			// System.out.println("dist diff " + distDiff
			// + " weight diff " + Math.abs(a.w - b.w) + "\n");
			// }
			// }
		}
	}

	private void render() {
		GL11.glClear(GL11.GL_COLOR_BUFFER_BIT);
		GL11.glClearColor(1, 1, 1, 1);

		MBGL.enableBlend();

		GL11.glPointSize(5.0f);
		GL11.glColor4f(1, 0, 0, 1);
		GL11.glBegin(GL11.GL_POINTS);
		for (int i = 0; i < points.size(); i++)
			GL11.glVertex2f(points.get(i).getX(), points.get(i).getY());
		GL11.glEnd();

		for (int i = 0; i < points.size(); i++)
			MBGL.renderEclipse(points.get(i).getX(), 0 , points.get(i).getY(),
					weights.get(i), weights.get(i), false);

		GL11.glLineWidth(1.0f);
		GL11.glColor4f(0, 0, 1, 1);
		if (voronoi != null) {
			float[] bound = voronoi.getBoundCoords();
			new Rectangle(bound[0], bound[1], bound[2], bound[3]).render();

			GL11.glPointSize(3.0f);
			if (voronoi.generators() != null) {
				GL11.glBegin(GL11.GL_POINTS);

				for (OkabeVoronoiCell cell : voronoi.generators)
					for (Point2D vertex : cell.getEdgeVertices())
						GL11.glVertex3f(vertex.getX(), vertex.getY(), 0);
				GL11.glEnd();
			}
		}

		MBGL.disableBlend();
	}
}
